Breaking the Rules
In the “Wordle” clone project, we have a gameStatus
state variable:
function Game() { // running | won | lost const [gameStatus, setGameStatus] = React.useState('running'); const [guesses, setGuesses] = React.useState([]);}
A couple of students have realized that this variable can be derived! We can calculate it exclusively using the guesses
, using logic like this:
let gameStatus;
// Have they correctly guessed the answer?const hasCorrectGuess = guesses.some(guess => ( guess === answer));
if (hasCorrectGuess) { // If so, it means they've won! gameStatus = 'won';} else if (guesses.length === 6) { // If they have submitted 6 guesses, and none // of them are correct, it means they've lost gameStatus = 'lost';} else { // With no right answer and at least 1 // guess remaining, the game continues! gameStatus = 'running';}
The golden rule we've been following is that if something can be derived from state, it should be derived from state. And as it turns out, we can derive the game's status from the guesses
state variable!
But hmm… I'm not sure I prefer this approach. 😅
The status of the game seems like its own “thing”, semantically. It feels coincidental that it can be derived from the guesses
array, that it just so happens to work based on the current game functionality.
I can imagine lots of ways the game could evolve which would make it impossible to derive the status:
- We might want to have an intro/welcome screen. The initial status would be something like
initial
, and it would flip torunning
when they click a start button. - In the real Wordle game, input is disabled for a second or two after submitting a guess, so that an animation can run. This could be implemented as a separate status (
revealing-guess
). - If we add a timer to the game, and wanted to integrate the ability to "pause" things. We'd need a new status,
paused
.
Granted, none of those issues exist today, and I don't generally advocate for “pre-emptive” problem-solving! We can always refactor things if/when the game evolves in one of these ways.
But even if we say that the game is 100% complete, that we'll never add any of these features… I'd still choose to keep gameStatus
as a state variable, rather than deriving it from guesses
.
In my mental model, gameStatus
is a separate concern. I don't think of it merely as a consequence of the user's guesses! By deriving gameStatus
from guesses
, it's like I'm tying the two things together when they should be independent. I want my code to reflect my mental model.
Ultimately, you might disagree, and that's totally valid. When it comes to our mental model, there is no right/wrong answer. I don't necessarily care to convince you that gameStatus
shouldn't be derived.
The more-important takeaway is that it's OK to break the rules sometimes, as long as you're doing it intentionally, and with confidence.
As I mentioned in the intro, there are no “best practices”. Deriving state is a good idea 95% of the time, but there are always going to be exceptions! That's why it's important to know why these patterns exist.
That deep understanding takes time to develop. When I taught at a bootcamp, I'd tell students to follow the recommended practices 100% of the time. As you become more experienced, however, you'll start to run into situations where the “best practices” feel like a bit of an awkward fit to you. In cases like this, you should absolutely do what feels right!